package gecko.frame;

import gecko.lang.JSONObject;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;

/**
 * 数据帧解析
 *
 * @author 陈哈哈 (yoojiachen@gmail.com, chenyongjia@parkingwang.com)
 * @version 0.1
 */
public class FrameAdapter {

    private final List<FrameField> mFields = new ArrayList<>();

    /**
     * 添加字段定义
     *
     * @param m FrameField
     * @return FrameAdapter
     */
    public FrameAdapter addField(FrameField m) {
        mFields.add(m);
        return this;
    }

    /**
     * 解析数据，转换成JSONObject
     *
     * @param bytes 数据
     * @return JSONObject
     */
    public JSONObject parse(byte[] bytes) {
        return parse(bytes, ByteOrder.BIG_ENDIAN);
    }

    /**
     * 解析数据，转换成JSONObject
     *
     * @param bytes     数据
     * @param byteOrder 字节序
     * @return JSONObject
     */
    public JSONObject parse(byte[] bytes, ByteOrder byteOrder) {
        final int totalSize = mFields.stream().map(f -> f.byteSize).reduce(0, (a, b) -> a + b);
        if (bytes.length < totalSize) {
            throw new IllegalArgumentException("Bytes not match: " + bytes.length + ", at least: " + totalSize);
        }
        JSONObject out = new JSONObject(mFields.size());
        final ByteBuffer buffer = ByteBuffer.wrap(bytes).order(byteOrder);
        for (FrameField f : mFields) {
            switch (f.type) {
                case BYTE:
                    out.field(f.name, f.converter.convert(f.type, buffer.get()));
                    break;

                case SHORT:
                    out.field(f.name, f.converter.convert(f.type, buffer.getShort()));
                    break;

                case INT:
                    out.field(f.name, f.converter.convert(f.type, buffer.getInt()));
                    break;

                case ARRAY:
                    final byte[] tmp = new byte[f.byteSize];
                    buffer.get(tmp);
                    out.field(f.name, f.converter.convert(f.type, tmp));
                    break;
            }
        }
        return out;
    }

}
